package core.command;

import java.util.ArrayList;
import java.util.Stack;

public class Memento {

	private Stack<AbstractCommand> doneCommands;
	private Stack<AbstractCommand> undoneCommands;
	
	public Memento() {
		this.doneCommands = new Stack<AbstractCommand>();
		this.undoneCommands = new Stack<AbstractCommand>();
	}
	
	public void doCommand(AbstractCommand cmd, boolean mergeWithLast) {
		cmd.execute();
		if (mergeWithLast) {
			this.mergeCommands(this.doneCommands.peek(), cmd);
		} else {
			this.doneCommands.push(cmd);
			this.undoneCommands.clear();
		}
	}
	
	public void redo() {
		if (canRedo()) {
			AbstractCommand cmd = this.undoneCommands.pop();
			cmd.execute();
			this.doneCommands.push(cmd);
		}
	}
	
	public void undo() {
		if (canUndo()) {
			AbstractCommand cmd = this.doneCommands.pop();
			cmd.undo();
			this.undoneCommands.push(cmd);
		}
	}
	
	private void mergeCommands(AbstractCommand lastCmd, AbstractCommand newCmd) {
		if (lastCmd.getClass().equals(newCmd.getClass())) {
			if (lastCmd.getClass().toString().equals(TransformStrokesCommand.class.toString())) {
				this.mergeTransformStrokesCommands((TransformStrokesCommand)lastCmd, (TransformStrokesCommand)newCmd);
			} else if (lastCmd.getClass().toString().equals(TranslateStrokesCommand.class.toString())) {
				this.mergeTranslateStrokesCommands((TranslateStrokesCommand)lastCmd, (TranslateStrokesCommand)newCmd);
			}
			
			this.doneCommands.pop();
			this.doneCommands.push(newCmd);
		} else {
			this.doneCommands.push(newCmd);
			this.undoneCommands.clear();
		}
		// TODO remove the else clause
	}

	private void mergeTranslateStrokesCommands(TranslateStrokesCommand lastCmd, TranslateStrokesCommand newCmd) {
		newCmd.setCursorPreviousPosition(lastCmd.getCursorPreviousPosition());
	}

	private void mergeTransformStrokesCommands(TransformStrokesCommand lastCmd, TransformStrokesCommand newCmd) {
		ArrayList<StrokesTransformation> lastTransformations = lastCmd.getTransformations();
		for (StrokesTransformation strokesTransformation : lastTransformations) {
			newCmd.addTransformation(strokesTransformation);
		}
	}
	
	public boolean canUndo()
	{
		return (!this.doneCommands.isEmpty());
	}
	
	public boolean canRedo()
	{
		return (!this.undoneCommands.isEmpty());
	}
}
